/** @license
 *
 *  Copyright (C) 2012 K. Arthur Endsley (kaendsle@mtu.edu)
 *  Michigan Tech Research Institute (MTRI)
 *  3600 Green Court, Suite 100, Ann Arbor, MI, 48105
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

(function (global) {
	var beginsWith, endsWith, root, Wkt;

	// Establish the root object, window in the browser, or exports on the server
	root = this;

	/**
	 * @desc The Wkt namespace.
	 * @property    {String}    delimiter   - The default delimiter for separating components of atomic geometry (coordinates)
	 * @namespace
	 * @global
	 */
	Wkt = function (obj) {
		if (obj instanceof Wkt) return obj;
		if (!(this instanceof Wkt)) return new Wkt(obj);
		this._wrapped = obj;
	};

	// Following Underscore module pattern (http://underscorejs.org/docs/underscore.html)
	if (typeof exports !== 'undefined') {
		if (typeof module !== 'undefined' && module.exports) {
			exports = module.exports = Wkt;
		}
		exports.Wkt = Wkt;
	} else {
		root.Wkt = Wkt;
	}

	/**
	 * Returns true if the substring is found at the beginning of the string.
	 * @param   str {String}    The String to search
	 * @param   sub {String}    The substring of interest
	 * @return      {Boolean}
	 * @private
	 */
	beginsWith = function (str, sub) {
		return str.substring(0, sub.length) === sub;
	};

	/**
	 * Returns true if the substring is found at the end of the string.
	 * @param   str {String}    The String to search
	 * @param   sub {String}    The substring of interest
	 * @return      {Boolean}
	 * @private
	 */
	endsWith = function (str, sub) {
		return str.substring(str.length - sub.length) === sub;
	};

	/**
	 * The default delimiter for separating components of atomic geometry (coordinates)
	 * @ignore
	 */
	Wkt.delimiter = ' ';

	/**
	 * Determines whether or not the passed Object is an Array.
	 * @param   obj {Object}    The Object in question
	 * @return      {Boolean}
	 * @member Wkt.isArray
	 * @method
	 */
	Wkt.isArray = function (obj) {
		return !!(obj && obj.constructor === Array);
	};

	/**
	 * Removes given character String(s) from a String.
	 * @param   str {String}    The String to search
	 * @param   sub {String}    The String character(s) to trim
	 * @return      {String}    The trimmed string
	 * @member Wkt.trim
	 * @method
	 */
	Wkt.trim = function (str, sub) {
		sub = sub || ' '; // Defaults to trimming spaces
		// Trim beginning spaces
		while (beginsWith(str, sub)) {
			str = str.substring(1);
		}
		// Trim ending spaces
		while (endsWith(str, sub)) {
			str = str.substring(0, str.length - 1);
		}
		return str;
	};

	/**
	 * An object for reading WKT strings and writing geographic features
	 * @constructor this.Wkt.Wkt
	 * @param   initializer {String}    An optional WKT string for immediate read
	 * @property            {Array}     components      - Holder for atomic geometry objects (internal representation of geometric components)
	 * @property            {String}    delimiter       - The default delimiter for separating components of atomic geometry (coordinates)
	 * @property            {Object}    regExes         - Some regular expressions copied from OpenLayers.Format.WKT.js
	 * @property            {String}    type            - The Well-Known Text name (e.g. 'point') of the geometry
	 * @property            {Boolean}   wrapVerticies   - True to wrap vertices in MULTIPOINT geometries; If true: MULTIPOINT((30 10),(10 30),(40 40)); If false: MULTIPOINT(30 10,10 30,40 40)
	 * @return              {this.Wkt.Wkt}
	 * @memberof Wkt
	 */
	Wkt.Wkt = function (initializer) {

		/**
		 * The default delimiter between X and Y coordinates.
		 * @ignore
		 */
		this.delimiter = Wkt.delimiter || ' ';

		/**
		 * Configuration parameter for controlling how Wicket seralizes
		 * MULTIPOINT strings. Examples; both are valid WKT:
		 * If true: MULTIPOINT((30 10),(10 30),(40 40))
		 * If false: MULTIPOINT(30 10,10 30,40 40)
		 * @ignore
		 */
		this.wrapVertices = true;

		/**
		 * Some regular expressions copied from OpenLayers.Format.WKT.js
		 * @ignore
		 */
		this.regExes = {
			'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
			'spaces': /\s+|\+/, // Matches the '+' or the empty space
			'numeric': /-*\d+(\.*\d+)?/,
			'comma': /\s*,\s*/,
			'parenComma': /\)\s*,\s*\(/,
			'coord': /-*\d+\.*\d+ -*\d+\.*\d+/, // e.g. "24 -14"
			'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/,
			'trimParens': /^\s*\(?(.*?)\)?\s*$/,
			'ogcTypes': /^(multi)?(point|line|polygon|box)?(string)?$/i, // Captures e.g. "Multi","Line","String"
			'crudeJson': /^{.*"(type|coordinates|geometries|features)":.*}$/ // Attempts to recognize JSON strings
		};

		/**
		 * The internal representation of geometry--the "components" of geometry.
		 * @ignore
		 */
		this.components = undefined;

		// An initial WKT string may be provided
		if (initializer && typeof initializer === 'string') {
			this.read(initializer);
		} else if (initializer && typeof initializer !== undefined) {
			this.fromObject(initializer);
		}

	};

	global.Wkt = Wkt;

	/**
	 * Returns true if the internal geometry is a collection of geometries.
	 * @return  {Boolean}   Returns true when it is a collection
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.isCollection = function () {
		switch (this.type.slice(0, 5)) {
		case 'multi':
			// Trivial; any multi-geometry is a collection
			return true;
		case 'polyg':
			// Polygons with holes are "collections" of rings
			return true;
		default:
			// Any other geometry is not a collection
			return false;
		}
	};
	
	/**
	 * Compares two x,y coordinates for equality.
	 * @param   a   {Object}    An object with x and y properties
	 * @param   b   {Object}    An object with x and y properties
	 * @return      {Boolean}
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.sameCoords = function (a, b) {
		return (a.x === b.x && a.y === b.y);
	};
	
	/**
	 * Sets internal geometry (components) from framework geometry (e.g.
	 * Google Polygon objects or google.maps.Polygon).
	 * @param   obj {Object}    The framework-dependent geometry representation
	 * @return      {this.Wkt.Wkt}   The object itself
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.fromObject = function (obj) {
		var result;
	
		if (obj.hasOwnProperty('type') && obj.hasOwnProperty('coordinates')) {
			result = this.fromJson(obj);
		} else {
			result = this.deconstruct.call(this, obj);
		}
	
		this.components = result.components;
		this.isRectangle = result.isRectangle || false;
		this.type = result.type;
		return this;
	};
	
	/**
	 * Creates external geometry objects based on a plug-in framework's
	 * construction methods and available geometry classes.
	 * @param   config  {Object}    An optional framework-dependent properties specification
	 * @return          {Object}    The framework-dependent geometry representation
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.toObject = function (config) {
		var obj = this.construct[this.type].call(this, config);
		// Don't assign the "properties" property to an Array
		if (typeof obj === 'object' && !Wkt.isArray(obj)) {
			obj.properties = this.properties;
		}
		return obj;
	};
	
	/**
	 * Returns the WKT string representation; the same as the write() method.
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.toString = function (config) {
		return this.write();
	};
	
	/**
	 * Parses a JSON representation as an Object.
	 * @param	obj	{Object}	An Object with the GeoJSON schema
	 * @return	{this.Wkt.Wkt}	The object itself
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.fromJson = function (obj) {
		var i, j, k, coords, iring, oring;
	
		this.type = obj.type.toLowerCase();
		this.components = [];
		if (obj.hasOwnProperty('geometry')) { //Feature
			this.fromJson(obj.geometry);
			this.properties = obj.properties;
			return this;
		}
		coords = obj.coordinates;
	
		if (!Wkt.isArray(coords[0])) { // Point
			this.components.push({
				x: coords[0],
				y: coords[1]
			});
	
		} else {
	
			for (i in coords) {
				if (coords.hasOwnProperty(i)) {
	
					if (!Wkt.isArray(coords[i][0])) { // LineString
	
						if (this.type === 'multipoint') { // MultiPoint
							this.components.push([{
								x: coords[i][0],
								y: coords[i][1]
							}]);
	
						} else {
							this.components.push({
								x: coords[i][0],
								y: coords[i][1]
							});
	
						}
	
					} else {
	
						oring = [];
						for (j in coords[i]) {
							if (coords[i].hasOwnProperty(j)) {
	
								if (!Wkt.isArray(coords[i][j][0])) {
									oring.push({
										x: coords[i][j][0],
										y: coords[i][j][1]
									});
	
								} else {
	
									iring = [];
									for (k in coords[i][j]) {
										if (coords[i][j].hasOwnProperty(k)) {
	
											iring.push({
												x: coords[i][j][k][0],
												y: coords[i][j][k][1]
											});
	
										}
									}
	
									oring.push(iring);
	
								}
	
							}
						}
	
						this.components.push(oring);
					}
				}
			}
	
		}
	
		return this;
	};
	
	/**
	 * Creates a JSON representation, with the GeoJSON schema, of the geometry.
	 * @return    {Object}    The corresponding GeoJSON representation
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.toJson = function () {
		var cs, json, i, j, k, ring, rings;
	
		cs = this.components;
		json = {
			coordinates: [],
			type: (function () {
				var i, type, s;
	
				type = this.regExes.ogcTypes.exec(this.type).slice(1);
				s = [];
	
				for (i in type) {
					if (type.hasOwnProperty(i)) {
						if (type[i] !== undefined) {
							s.push(type[i].toLowerCase().slice(0, 1).toUpperCase() + type[i].toLowerCase().slice(1));
						}
					}
				}
	
				return s;
			}.call(this)).join('')
		}
	
		// Wkt BOX type gets a special bbox property in GeoJSON
		if (this.type.toLowerCase() === 'box') {
			json.type = 'Polygon';
			json.bbox = [];
	
			for (i in cs) {
				if (cs.hasOwnProperty(i)) {
					json.bbox = json.bbox.concat([cs[i].x, cs[i].y]);
				}
			}
	
			json.coordinates = [
				[
					[cs[0].x, cs[0].y],
					[cs[0].x, cs[1].y],
					[cs[1].x, cs[1].y],
					[cs[1].x, cs[0].y],
					[cs[0].x, cs[0].y]
				]
			];
	
			return json;
		}
	
		// For the coordinates of most simple features
		for (i in cs) {
			if (cs.hasOwnProperty(i)) {
	
				// For those nested structures
				if (Wkt.isArray(cs[i])) {
					rings = [];
	
					for (j in cs[i]) {
						if (cs[i].hasOwnProperty(j)) {
	
							if (Wkt.isArray(cs[i][j])) { // MULTIPOLYGONS
								ring = [];
	
								for (k in cs[i][j]) {
									if (cs[i][j].hasOwnProperty(k)) {
										ring.push([cs[i][j][k].x, cs[i][j][k].y]);
									}
								}
	
								rings.push(ring);
	
							} else { // POLYGONS and MULTILINESTRINGS
	
								if (cs[i].length > 1) {
									rings.push([cs[i][j].x, cs[i][j].y]);
	
								} else { // MULTIPOINTS
									rings = rings.concat([cs[i][j].x, cs[i][j].y]);
								}
							}
						}
					}
	
					json.coordinates.push(rings);
	
				} else {
					if (cs.length > 1) { // For LINESTRING type
						json.coordinates.push([cs[i].x, cs[i].y]);
	
					} else { // For POINT type
						json.coordinates = json.coordinates.concat([cs[i].x, cs[i].y]);
					}
				}
	
			}
		}
	
		return json;
	};
	
	/**
	 * Absorbs the geometry of another this.Wkt.Wkt instance, merging it with its own,
	 * creating a collection (MULTI-geometry) based on their types, which must agree.
	 * For example, creates a MULTIPOLYGON from a POLYGON type merged with another
	 * POLYGON type, or adds a POLYGON instance to a MULTIPOLYGON instance.
	 * @param   wkt {String}    A Wkt.Wkt object
	 * @return	{this.Wkt.Wkt}	The object itself
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.merge = function (wkt) {
		var prefix = this.type.slice(0, 5);
	
		if (this.type !== wkt.type) {
			if (this.type.slice(5, this.type.length) !== wkt.type) {
				throw TypeError('The input geometry types must agree or the calling this.Wkt.Wkt instance must be a multigeometry of the other');
			}
		}
	
		switch (prefix) {
	
		case 'point':
			this.components = [this.components.concat(wkt.components)];
			break;
	
		case 'multi':
			this.components = this.components.concat((wkt.type.slice(0, 5) === 'multi') ? wkt.components : [wkt.components]);
			break;
	
		default:
			this.components = [
				this.components,
				wkt.components
			];
			break;
	
		}
	
		if (prefix !== 'multi') {
			this.type = 'multi' + this.type;
		}
		return this;
	};
	
	/**
	 * Reads a WKT string, validating and incorporating it.
	 * @param   str {String}    A WKT or GeoJSON string
	 * @return	{this.Wkt.Wkt}	The object itself
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.read = function (str) {
		var matches;
		matches = this.regExes.typeStr.exec(str);
		if (matches) {
			this.type = matches[1].toLowerCase();
			this.base = matches[2];
			if (this.ingest[this.type]) {
				this.components = this.ingest[this.type].apply(this, [this.base]);
			}
	
		} else {
			if (this.regExes.crudeJson.test(str)) {
				if (typeof JSON === 'object' && typeof JSON.parse === 'function') {
					this.fromJson(JSON.parse(str));
	
				} else {
					console.log('JSON.parse() is not available; cannot parse GeoJSON strings');
					throw {
						name: 'JSONError',
						message: 'JSON.parse() is not available; cannot parse GeoJSON strings'
					};
				}
	
			} else {
				console.log('Invalid WKT string provided to read()');
				throw {
					name: 'WKTError',
					message: 'Invalid WKT string provided to read()'
				};
			}
		}
	
		return this;
	}; // eo readWkt
	
	/**
	 * Writes a WKT string.
	 * @param   components  {Array}     An Array of internal geometry objects
	 * @return              {String}    The corresponding WKT representation
	 * @memberof this.Wkt.Wkt
	 * @method
	 */
	Wkt.Wkt.prototype.write = function (components) {
		var i, pieces, data;
	
		components = components || this.components;
	
		pieces = [];
	
		pieces.push(this.type.toUpperCase() + '(');
	
		for (i = 0; i < components.length; i += 1) {
			if (this.isCollection() && i > 0) {
				pieces.push(',');
			}
	
			// There should be an extract function for the named type
			if (!this.extract[this.type]) {
				return null;
			}
	
			data = this.extract[this.type].apply(this, [components[i]]);
			if (this.isCollection() && this.type !== 'multipoint') {
				pieces.push('(' + data + ')');
	
			} else {
				pieces.push(data);
	
				// If not at the end of the components, add a comma
				if (i !== (components.length - 1) && this.type !== 'multipoint') {
					pieces.push(',');
				}
	
			}
		}
	
		pieces.push(')');
	
		return pieces.join('');
	};
	
	/**
	 * This object contains functions as property names that extract WKT
	 * strings from the internal representation.
	 * @memberof this.Wkt.Wkt
	 * @namespace this.Wkt.Wkt.extract
	 * @instance
	 */
	Wkt.Wkt.prototype.extract = {
		/**
		 * Return a WKT string representing atomic (point) geometry
		 * @param   point   {Object}    An object with x and y properties
		 * @return          {String}    The WKT representation
		 * @memberof this.Wkt.Wkt.extract
		 * @instance
		 */
		point: function (point) {
			return String(point.x) + this.delimiter + String(point.y);
		},
	
		/**
		 * Return a WKT string representing multiple atoms (points)
		 * @param   multipoint  {Array}     Multiple x-and-y objects
		 * @return              {String}    The WKT representation
		 * @memberof this.Wkt.Wkt.extract
		 * @instance
		 */
		multipoint: function (multipoint) {
			var i, parts = [],
				s;
	
			for (i = 0; i < multipoint.length; i += 1) {
				s = this.extract.point.apply(this, [multipoint[i]]);
	
				if (this.wrapVertices) {
					s = '(' + s + ')';
				}
	
				parts.push(s);
			}
	
			return parts.join(',');
		},
	
		/**
		 * Return a WKT string representing a chain (linestring) of atoms
		 * @param   linestring  {Array}     Multiple x-and-y objects
		 * @return              {String}    The WKT representation
		 * @memberof this.Wkt.Wkt.extract
		 * @instance
		 */
		linestring: function (linestring) {
			// Extraction of linestrings is the same as for points
			return this.extract.point.apply(this, [linestring]);
		},
	
		/**
		 * Return a WKT string representing multiple chains (multilinestring) of atoms
		 * @param   multilinestring {Array}     Multiple of multiple x-and-y objects
		 * @return                  {String}    The WKT representation
		 * @memberof this.Wkt.Wkt.extract
		 * @instance
		 */
		multilinestring: function (multilinestring) {
			var i, parts = [];
	
			for (i = 0; i < multilinestring.length; i += 1) {
				parts.push(this.extract.linestring.apply(this, [multilinestring[i]]));
			}
	
			return parts.join(',');
		},
	
		/**
		 * Return a WKT string representing multiple atoms in closed series (polygon)
		 * @param   polygon {Array}     Collection of ordered x-and-y objects
		 * @return          {String}    The WKT representation
		 * @memberof this.Wkt.Wkt.extract
		 * @instance
		 */
		polygon: function (polygon) {
			// Extraction of polygons is the same as for multilinestrings
			return this.extract.multilinestring.apply(this, [polygon]);
		},
	
		/**
		 * Return a WKT string representing multiple closed series (multipolygons) of multiple atoms
		 * @param   multipolygon    {Array}     Collection of ordered x-and-y objects
		 * @return                  {String}    The WKT representation
		 * @memberof this.Wkt.Wkt.extract
		 * @instance
		 */
		multipolygon: function (multipolygon) {
			var i, parts = [];
			for (i = 0; i < multipolygon.length; i += 1) {
				parts.push('(' + this.extract.polygon.apply(this, [multipolygon[i]]) + ')');
			}
			return parts.join(',');
		},
	
		/**
		 * Return a WKT string representing a 2DBox
		 * @param   multipolygon    {Array}     Collection of ordered x-and-y objects
		 * @return                  {String}    The WKT representation
		 * @memberof this.Wkt.Wkt.extract
		 * @instance
		 */
		box: function (box) {
			return this.extract.linestring.apply(this, [box]);
		},
	
		geometrycollection: function (str) {
			console.log('The geometrycollection WKT type is not yet supported.');
		}
	};
	
	/**
	 * This object contains functions as property names that ingest WKT
	 * strings into the internal representation.
	 * @memberof this.Wkt.Wkt
	 * @namespace this.Wkt.Wkt.ingest
	 * @instance
	 */
	Wkt.Wkt.prototype.ingest = {
	
		/**
		 * Return point feature given a point WKT fragment.
		 * @param   str {String}    A WKT fragment representing the point
		 * @memberof this.Wkt.Wkt.ingest
		 * @instance
		 */
		point: function (str) {
			var coords = Wkt.trim(str).split(this.regExes.spaces);
			// In case a parenthetical group of coordinates is passed...
			return [{ // ...Search for numeric substrings
				x: parseFloat(this.regExes.numeric.exec(coords[0])[0]),
				y: parseFloat(this.regExes.numeric.exec(coords[1])[0])
			}];
		},
	
		/**
		 * Return a multipoint feature given a multipoint WKT fragment.
		 * @param   str {String}    A WKT fragment representing the multipoint
		 * @memberof this.Wkt.Wkt.ingest
		 * @instance
		 */
		multipoint: function (str) {
			var i, components, points;
			components = [];
			points = Wkt.trim(str).split(this.regExes.comma);
			for (i = 0; i < points.length; i += 1) {
				components.push(this.ingest.point.apply(this, [points[i]]));
			}
			return components;
		},
	
		/**
		 * Return a linestring feature given a linestring WKT fragment.
		 * @param   str {String}    A WKT fragment representing the linestring
		 * @memberof this.Wkt.Wkt.ingest
		 * @instance
		 */
		linestring: function (str) {
			var i, multipoints, components;
	
			// In our x-and-y representation of components, parsing
			//  multipoints is the same as parsing linestrings
			multipoints = this.ingest.multipoint.apply(this, [str]);
	
			// However, the points need to be joined
			components = [];
			for (i = 0; i < multipoints.length; i += 1) {
				components = components.concat(multipoints[i]);
			}
			return components;
		},
	
		/**
		 * Return a multilinestring feature given a multilinestring WKT fragment.
		 * @param   str {String}    A WKT fragment representing the multilinestring
		 * @memberof this.Wkt.Wkt.ingest
		 * @instance
		 */
		multilinestring: function (str) {
			var i, components, line, lines;
			components = [];
	
			lines = Wkt.trim(str).split(this.regExes.doubleParenComma);
			if (lines.length === 1) { // If that didn't work...
				lines = Wkt.trim(str).split(this.regExes.parenComma);
			}
	
			for (i = 0; i < lines.length; i += 1) {
				line = lines[i].replace(this.regExes.trimParens, '$1');
				components.push(this.ingest.linestring.apply(this, [line]));
			}
	
			return components;
		},
	
		/**
		 * Return a polygon feature given a polygon WKT fragment.
		 * @param   str {String}    A WKT fragment representing the polygon
		 * @memberof this.Wkt.Wkt.ingest
		 * @instance
		 */
		polygon: function (str) {
			var i, j, components, subcomponents, ring, rings;
			rings = Wkt.trim(str).split(this.regExes.parenComma);
			components = []; // Holds one or more rings
			for (i = 0; i < rings.length; i += 1) {
				ring = rings[i].replace(this.regExes.trimParens, '$1').split(this.regExes.comma);
				subcomponents = []; // Holds the outer ring and any inner rings (holes)
				for (j = 0; j < ring.length; j += 1) {
					// Split on the empty space or '+' character (between coordinates)
					var split=ring[j].split(this.regExes.spaces);
					if(split.length>2){
						//remove the elements which are blanks
						split = split.filter(function(n){ return n != "" });
					}
					if(split.length===2){
						var x_cord=split[0];
						var y_cord=split[1];
						
						//now push
						subcomponents.push({
							x: parseFloat(x_cord),
							y: parseFloat(y_cord)
						});
					}
				}
				components.push(subcomponents);
			}
			return components;
		},
	
		/**
		 * Return box vertices (which would become the Rectangle bounds) given a Box WKT fragment.
		 * @param   str {String}    A WKT fragment representing the box
		 * @memberof this.Wkt.Wkt.ingest
		 * @instance
		 */
		box: function (str) {
			var i, multipoints, components;
	
			// In our x-and-y representation of components, parsing
			//  multipoints is the same as parsing linestrings
			multipoints = this.ingest.multipoint.apply(this, [str]);
	
			// However, the points need to be joined
			components = [];
			for (i = 0; i < multipoints.length; i += 1) {
				components = components.concat(multipoints[i]);
			}
	
			return components;
		},
	
		/**
		 * Return a multipolygon feature given a multipolygon WKT fragment.
		 * @param   str {String}    A WKT fragment representing the multipolygon
		 * @memberof this.Wkt.Wkt.ingest
		 * @instance
		 */
		multipolygon: function (str) {
			var i, components, polygon, polygons;
			components = [];
			polygons = Wkt.trim(str).split(this.regExes.doubleParenComma);
			for (i = 0; i < polygons.length; i += 1) {
				polygon = polygons[i].replace(this.regExes.trimParens, '$1');
				components.push(this.ingest.polygon.apply(this, [polygon]));
			}
			return components;
		},
	
		/**
		 * Return an array of features given a geometrycollection WKT fragment.
		 * @param   str {String}    A WKT fragment representing the geometry collection
		 * @memberof this.Wkt.Wkt.ingest
		 * @instance
		 */
		geometrycollection: function (str) {
			console.log('The geometrycollection WKT type is not yet supported.');
		}
	
	}; // eo ingest
	
	return this;
}(this));

